package com.im.tcp.receiver;


import com.alibaba.fastjson.JSONObject;
import com.ecode.imecode.pack.MessagePack;
import com.im.tcp.utils.SessionSocketHolder;
import com.lld.im.common.constants.Constants;
import com.lld.im.common.enums.ClientType;
import com.lld.im.common.enums.DeviceMultiLoginEnum;
import com.lld.im.common.enums.commond.SystemCommand;
import com.lld.im.common.model.UserClientDto;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;

import java.util.List;

/**
 * 用户登录消息监听器【基于redis发布订阅模式】
 *
 * 多段登录机制 对于同类型的客户端登录 前者会被强制下线
 *
 * 设计思路: 在用户登录时 记录当前用户的userid clientType和imei保存在redis以及系统缓存中 当用户在别的服务器登录时 通过广播机制(redis发布订阅模式)
 *      发送给所有在线服务器 各个而服务器根据userid clientType和imei 判断 从而达到强制下线的效果
 */
@Slf4j
public class UserLoginMsgReceiver {

    private Integer loginMode;
    private RedissonClient redissonClient;

    public UserLoginMsgReceiver(Integer loginMode, RedissonClient redissonClient) {
        this.loginMode = loginMode;
        this.redissonClient = redissonClient;
    }

    //监听用户登录事件 这里会监听所有服务器(集群)的登录消息
    //基于redis的发布订阅模式
    //处理用户的登录消息
    public void userLoginListener(){
        redissonClient.getTopic(Constants.RedissonConfig.LOGIN_USER_CHANNEL).addListener(String.class,
                (charSequence, s) -> {
                   log.info("收到消息:{}",s);
                    // 获得当前userId/appId 在服务器内存中的所有channel
                    if (!StringUtils.isBlank(s)) {
                        handlerMsg(s);
                    }
                });
    }

    private void handlerMsg(String s) {
        UserClientDto userClientDto = JSONObject.parseObject(s, UserClientDto.class);
        List<Channel> channels = SessionSocketHolder.getUserSessionChannelByUserId(userClientDto.getAppId(),
                userClientDto.getUserId());
        //TODO 获得channel中保存的用户信息 根据clientType 以及 imei 判断
        for (Channel channel : channels) {
            Integer clientType = (Integer) channel.attr(AttributeKey.valueOf(Constants.CLIENT_TYPE)).get();
            String imei = (String) channel.attr(AttributeKey.valueOf(Constants.IMEI)).get();
            if(loginMode== DeviceMultiLoginEnum.ONE.getLoginMode()){
                if(!clientType.equals(userClientDto.getClientType())
                        &&!imei.equals(userClientDto.getImei())){
                    //TODO 当前服务器已经存在不相同clientType和imei 用户已经登录过了 踢出下线
                    offlineCommandSendClient(channel);
                    break;
                }
            } else if (loginMode == DeviceMultiLoginEnum.TWO.getLoginMode()) {
                //如果是web端 不限制登录
               if(clientType==ClientType.WEB.getCode()||
                       userClientDto.getClientType()==ClientType.WEB.getCode()){
                   continue;
               }
                if(!clientType.equals(userClientDto.getClientType())
                        &&!imei.equals(userClientDto.getImei())){
                    //TODO 踢出下线 并不是直接断开连接 而是给予用户提醒
                    offlineCommandSendClient(channel);
                    break;
                }
            }else if(loginMode==DeviceMultiLoginEnum.THREE.getLoginMode()){
                //不会拦截web登录
                if(clientType==ClientType.WEB.getCode()
                        ||userClientDto.getClientType()==ClientType.WEB.getCode())
                    continue;
                //对于三段登录 手机/电脑/web端
                boolean isSameClient=false;
                //判断是否为同端登录 对于ios/Android   windows/mac 属于同一端
                if(clientType==ClientType.IOS.getCode()||clientType==ClientType.ANDROID.getCode()
                        &&userClientDto.getClientType()==ClientType.IOS.getCode()
                        ||userClientDto.getClientType()==ClientType.ANDROID.getCode()){
                    isSameClient=true;
                }
                if(clientType==ClientType.MAC.getCode()||clientType==ClientType.WINDOWS.getCode()
                        &&userClientDto.getClientType()==ClientType.MAC.getCode()
                        ||userClientDto.getClientType()==ClientType.WINDOWS.getCode()){
                    isSameClient=true;
                }

                if(isSameClient){
                    if(!clientType.equals(userClientDto.getClientType())
                            &&!imei.equals(userClientDto.getImei())){
                        //TODO 踢出下线
                        offlineCommandSendClient(channel);
                        break;
                    }
                }
            }
        }

    }

    /**
     * 下线通知
     * @param channel
     */
    private void offlineCommandSendClient(Channel channel) {
        MessagePack messagePack = new MessagePack();
        messagePack.setToUserId(channel.attr(AttributeKey.valueOf(Constants.USERID)).get().toString());
        messagePack.setToAppId(channel.attr(AttributeKey.valueOf(Constants.APPID)).get().toString());
        messagePack.setToClientType(channel.attr(AttributeKey.valueOf(Constants.CLIENT_TYPE)).get().toString());
        messagePack.setToImei(channel.attr(AttributeKey.valueOf(Constants.IMEI)).get().toString());
        messagePack.setCommand(SystemCommand.MULTI_LOGIN.getCommand());
        channel.writeAndFlush(JSONObject.toJSONString(messagePack));
        log.info("{} 用户多端登录 {} 被踢出下线",messagePack.getToUserId(),messagePack.getToClientType());
    }

    public static void init(Integer loginMode,RedissonClient redissonClient){
        new UserLoginMsgReceiver(loginMode, redissonClient).userLoginListener();
    }
}
